/**
*	@file PKB.cpp
*
*	@brief this header will include all the PKB component
*
*	@author li yuchen
*
*	@date 1/28/2011
*/

#include "PKB.h"
#include "Utility.h"
#include <fstream>
#include <string>
#include <sstream>
#include <iostream>
#include <direct.h>
#include <stdlib.h>
#include <stdio.h>

static string DrawCFGHelper(GNode* gnode_a, GNode* gnode_b, StmtNum a_start, StmtNum a_end, StmtNum b_start, StmtNum b_end){
	stringstream ss;
	ss << "\"";
	switch(gnode_a->g_type){
	case IF_TYPE:
		ss << "If" << "(" << gnode_a->stmt_num << ")";
		break;
	case WHILE_TYPE:
		ss << "While" << "(" << gnode_a->stmt_num << ")";
		break;

	default:
		if(a_start != a_end) ss << "Stmt" << "(" << a_start << "~" << a_end << ")";
		else ss << "Stmt" << "(" << a_start << ")";
	}
	ss << "\"->\"";
	if(gnode_b == NULL){
		ss << "End(Proc[" << b_start << "])\";";
		return ss.str();
	}
	switch(gnode_b->g_type){
	case IF_TYPE:
		ss << "If" << "(" << gnode_b->stmt_num << ")";
		break;
	case WHILE_TYPE:
		ss << "While" << "(" << gnode_b->stmt_num << ")";
		break;
	default:
		if(b_start != b_end) ss << "Stmt" << "(" << b_start << "~" << b_end << ")";
		else ss << "Stmt" << "(" << b_start << ")";
	}
	ss << "\";";
	ss << endl;
	return ss.str();
}

static void DrawCFGTraverse(GNode* traverser, StmtNum& start, StmtNum& end){
	start = traverser->stmt_num;
	while(traverser->next_a != NULL){
		if(traverser->next_a->g_type == IF_TYPE || traverser->next_a->g_type == WHILE_TYPE) break;
		if(StmtTable::GetStmtTNode(traverser->next_a->stmt_num)->left_sibling != NULL){
			if(StmtTable::GetStmtTNode(traverser->next_a->stmt_num)->left_sibling->type == IF_TYPE) break;
		}
		traverser = traverser->next_a;
	}
	end = traverser->stmt_num;
}

void PKB::DrawAST(){
	char* path = NULL;
	path = _getcwd( NULL, 0);
	string digram_text_file(path);
	digram_text_file.append("\\AST.dot");

	ofstream text_output(digram_text_file,ios::out);
	text_output << "digraph G {" << endl;

	stack<TNode*> tnode_stack;

	tnode_stack.push(AST::get_p_tnode_AST_root());
	TNode* level_traverse = NULL;
	TNode* level_root = NULL;

	do{
		// get a element from the stack
		level_root = tnode_stack.top(); 
		level_traverse = level_root->child;
		tnode_stack.pop();


		// link next level children
		while(level_traverse != NULL){		
			stringstream parent_child;
			// hold result for each line
			string t1;

			// handle current root tnode
			switch(level_root->type){
			case ROOT:
				parent_child << "\"root" <<"\"";
				break;
			case PROC_TYPE:
				parent_child << "\"procedure:" << ProcTable::GetProcName(level_root->value) << "\"";
				break;
			case STMT_LIST_TYPE:
				parent_child << "\"stmtlist:" << level_root->stmt_num << "\""; 
				break;
			case ASSIGN_TYPE:
				parent_child << "\"assignment:" << level_root->stmt_num <<"\"";
				break;
			case WHILE_TYPE:
				parent_child << "\"while:" << level_root->stmt_num << "\"";
				break;
			case IF_TYPE:
				parent_child << "\"if:" << level_root->stmt_num << "\"";
				break;
			case OP_TYPE:
				parent_child << "\"operator:" << (char)level_root-> value<< "(" << level_root->stmt_num  << ")\"";
				break;
			case VAR_TYPE:
				break;
			case CONST_TYPE:
				break;
			default:
				cout << "parent error!" << endl;
				getchar();
				exit(1);
			}
			
			// handle the child
			if(level_traverse != NULL){
				parent_child << "->";
				switch(level_traverse->type){
				case PROC_TYPE:
					parent_child << "\"procedure:" << ProcTable::GetProcName(level_traverse->value) << "\"";
					break;
				case STMT_LIST_TYPE:
					parent_child << "\"stmtlist:" <<  level_traverse->stmt_num << "\""; 
					break;
				case ASSIGN_TYPE:
					parent_child << "\"assignment:" << level_traverse->stmt_num << "\"";
					break;
				case WHILE_TYPE:
					parent_child << "\"while:" << level_traverse->stmt_num << "\"";
					break;
				case IF_TYPE:
					parent_child << "\"if:" << level_traverse->stmt_num << "\"";
					break;
				case OP_TYPE:
					parent_child << "\"operator:" << (char)level_traverse->value << "(" << level_traverse->stmt_num << ")\"";
					break;
				case VAR_TYPE:
					parent_child << "\"variable:" << VarTable::GetVarName(level_traverse->value) << "(" << level_traverse->stmt_num << ")\"" ;
					break;
				case CONST_TYPE:
					parent_child << "\"constant:" << level_traverse->value << "(" << level_traverse->stmt_num << ")\"";
					break;
				case CALL_TYPE:
					parent_child << "\"call:" << ProcTable::GetProcName(level_traverse->value) << "(" << level_traverse->stmt_num << ")\"";
					break;
				default:
					cout << "child error!" << endl;
					getchar();
					exit(1);
				}
				parent_child >> t1;
				text_output << t1 << ";"  << endl;

				// push the child for the next level
				tnode_stack.push(level_traverse);

			}// end if-child

			level_traverse = level_traverse->right_sibling;

		}// end while for each level

	}while(!tnode_stack.empty());

	text_output << "}" << endl;

	// close the file
	text_output.close();

	// use Graphviz to draw the digram
	
	string AST_png_output_file(path);
	AST_png_output_file.append("\\AST.png");
	string graphviz_command("dot.exe");
	graphviz_command.append(" -Tpng ").append(digram_text_file).append(" -o ").append(AST_png_output_file);

	cout << "generating graph!" << endl;
	system(graphviz_command.c_str());

	string windows_viewer_command("rundll32 \"C:\\Program Files\\Windows Photo Viewer\\PhotoViewer.dll\", ImageView_Fullscreen ");
	windows_viewer_command.append(AST_png_output_file);
	system(windows_viewer_command.c_str());
}

void PKB::DrawCFG(){
	char* path = NULL;
	path = _getcwd( NULL, 0);
	string digram_text_file(path);
	digram_text_file.append("\\CFG.dot");

	ofstream text_output(digram_text_file,ios::out);
	text_output << "digraph G {" << endl;

	for(int i = 0; i < ProcTable::GetSize(); i++){
		GNode* traverser = ProcTable::GetProcCFGRoot(i+1);
		GNode* next_gnode = NULL;
		vector<GNode*> gnode_stack;
		gnode_stack.push_back(traverser);

		vector<bool> traversed(StmtTable::GetSize(),false);

		while(gnode_stack.size() != 0){
			string text;
			traverser = gnode_stack[gnode_stack.size()-1];
			next_gnode = NULL;
			gnode_stack.pop_back();
			StmtNum start;
			StmtNum end;

			if(traverser->g_type == WHILE_TYPE || traverser->g_type == IF_TYPE){
				if(traversed[traverser->stmt_num-1]) continue;
				traversed[traverser->stmt_num-1] = true;
				gnode_stack.push_back(traverser->next_a);

				next_gnode = traverser->next_a;
				DrawCFGTraverse(next_gnode,start,end);
				text = DrawCFGHelper(traverser,next_gnode,-1,-1,start,end);
				text_output << text;

				if(traverser->next_b != NULL){
					gnode_stack.push_back(traverser->next_b);
					next_gnode = traverser->next_b;
					DrawCFGTraverse(next_gnode,start,end);
					text = DrawCFGHelper(traverser,next_gnode,-1,-1,start,end);
					text_output << text;
				}
			}
			else{ 
				DrawCFGTraverse(traverser,start,end);
				if(traversed[start-1]) continue;
				else traversed[start-1] = true;
				next_gnode = StmtTable::GetStmtGNode(end)->next_a;
				if(next_gnode == NULL){
					text_output << DrawCFGHelper(traverser,NULL,start,end,i,-1);
					continue;
				}
				if(!traversed[next_gnode->stmt_num-1]){
					gnode_stack.push_back(next_gnode);
					//traversed[next_gnode->stmt_num-1] = true;
				}
				StmtNum start_temp;
				StmtNum end_temp;
				DrawCFGTraverse(next_gnode,start_temp,end_temp);
				text = DrawCFGHelper(traverser,next_gnode,start,end,start_temp,end_temp);
				text_output << text;
			}

		}
	}
	text_output << "}" << endl;

	// close the file
	text_output.close();
	
	// use Graphviz to draw the digram
	string CFG_png_output_file(path);
	CFG_png_output_file.append("\\CFG.png");
	string graphviz_command("dot.exe");
	graphviz_command.append(" -Tpng ").append(digram_text_file).append(" -o ").append(CFG_png_output_file);

	cout << "generating graph!" << endl;
	system(graphviz_command.c_str());

	string windows_viewer_command("rundll32 \"C:\\Program Files\\Windows Photo Viewer\\PhotoViewer.dll\", ImageView_Fullscreen ");
	windows_viewer_command.append(CFG_png_output_file);
	system(windows_viewer_command.c_str());
	
}
